//
//  InAppReceiptRefreshRequest.swift
//  Distance
//
//  Created by changhe on 2025/4/27.
//

import Foundation
import StoreKit

/// 收据类型
public enum YLReceiptType {
	/// 传统收据 (iOS 17及以下)
	case legacy(Data)
	/// JSON收据 (iOS 18及以上)
	case json(Data)
}

/// 收据获取结果
public enum YLReceiptResult {
	case success(YLReceiptType)
	case failure(Error?)
}

/// 收据回调类型
public typealias YLReceiptCompletion = (YLReceiptResult) -> Void

/// 收据管理器 - 获取和刷新收据
public class YLReceiptRefreshRequest {
	
	// MARK: - 初始化
	public init() {}
	
	// MARK: - 公共方法
	
	/// 获取收据
	/// - Parameters:
	///   - forceRefresh: 是否强制刷新收据
	///   - version: 使用的API版本
	///   - completion: 完成回调
	public func getReceipt(forceRefresh: Bool = false,
						   version: YLIAPVersion = .auto,
						   completion: @escaping YLReceiptCompletion) {
		
		if forceRefresh {
			refreshReceipt(version: version) { [weak self] success in
				if success {
					self?.getReceiptInternal(version: version, completion: completion)
				} else {
					completion(.failure(nil))
				}
			}
			return
		}
		
		getReceiptInternal(version: version, completion: completion)
	}
	
	// MARK: - 内部实现
	
	/// 内部获取收据方法
	private func getReceiptInternal(version: YLIAPVersion, completion: @escaping YLReceiptCompletion) {
		if #available(iOS 18.0, *), version == .v2 || version == .auto {
			getJSONReceiptV2(completion: completion)
			return
		}
		
		getLegacyReceipt(completion: completion)
	}
	
	/// 刷新收据
	private func refreshReceipt(version: YLIAPVersion, completion: @escaping (Bool) -> Void) {
		if #available(iOS 15.0, *), version == .v2 || version == .auto {
			Task {
				do {
					_ = try await AppStore.sync()
					await MainActor.run {
						completion(true)
					}
				} catch {
					if version == .auto {
						await MainActor.run {
							self.refreshReceiptV1(completion: completion)
						}
					} else {
						await MainActor.run {
							completion(false)
						}
					}
				}
			}
		} else {
			refreshReceiptV1(completion: completion)
		}
	}
	
	/// 获取传统收据 (V1和V2 iOS15-17共用)
	private func getLegacyReceipt(completion: @escaping YLReceiptCompletion) {
		guard let receiptURL = Bundle.main.appStoreReceiptURL,
			  FileManager.default.fileExists(atPath: receiptURL.path) else {
			completion(.failure(nil))
			return
		}
		
		DispatchQueue.global(qos: .userInitiated).async {
			do {
				let receiptData = try Data(contentsOf: receiptURL)
				
				if receiptData.isEmpty {
					DispatchQueue.main.async {
						completion(.failure(nil))
					}
					return
				}
				
				DispatchQueue.main.async {
					completion(.success(.legacy(receiptData)))
				}
				
			} catch {
				DispatchQueue.main.async {
					completion(.failure(error))
				}
			}
		}
	}
	
	/// 刷新V1收据
	private func refreshReceiptV1(completion: @escaping (Bool) -> Void) {
		let request = SKReceiptRefreshRequest(receiptProperties: nil)
		let delegate = ReceiptRefreshDelegate(completion: completion)
		
		objc_setAssociatedObject(request, "delegate", delegate, .OBJC_ASSOCIATION_RETAIN)
		
		request.delegate = delegate
		request.start()
	}
	
	/// 获取JSON格式收据 (iOS 18+)
	@available(iOS 18.0, *)
	private func getJSONReceiptV2(completion: @escaping YLReceiptCompletion) {
		Task {
			do {
				let appTransaction = try await AppTransaction.shared
				
				if case .verified(let verifiedAppTransaction) = appTransaction {
					let jsonData = verifiedAppTransaction.jsonRepresentation
					await MainActor.run {
						completion(.success(.json(jsonData)))
					}
					return
				}
				
				await MainActor.run {
					completion(.failure(nil))
				}
				
			} catch {
				await MainActor.run {
					completion(.failure(error))
				}
			}
		}
	}
}

// MARK: - 收据刷新代理

private class ReceiptRefreshDelegate: NSObject, SKRequestDelegate {
	private let completion: (Bool) -> Void
	
	init(completion: @escaping (Bool) -> Void) {
		self.completion = completion
		super.init()
	}
	
	func requestDidFinish(_ request: SKRequest) {
		completion(true)
	}
	
	func request(_ request: SKRequest, didFailWithError error: Error) {
		print("[YLReceiptManager] 刷新收据失败: \(error.localizedDescription)")
		completion(false)
	}
}

